package algorithm.winnow;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import core.Main;

import stemmer.NullStemmer;
import stemmer.Stemmer;
import utility.file.TextFile;
import wordnet.IWordnetOperator;
import wordnet.NullWordnetOperator;
import words.extractor.WordsExtractor;
import algorithm.measures.BinaryConfusionMatrix;


public class TestWinnowCommittee {
	
	private String _category;
	private double _thresholdCoefficient;
	private Map<String,Double> _expertToWeight;
	private Stemmer _stemmer;
	private IWordnetOperator _wordnet;
	private BinaryConfusionMatrix _matrix;

	public TestWinnowCommittee(String category, double thresholdCoefficient, IWordnetOperator catcher, Stemmer stemmer){
		_wordnet = catcher;
		_stemmer = stemmer;
		_category = category;
		_thresholdCoefficient = thresholdCoefficient;
		_expertToWeight = new HashMap<String,Double>();
		_matrix = new BinaryConfusionMatrix();
	}
	
	public TestWinnowCommittee(String category, double thresholdCoefficient){
		this(category,thresholdCoefficient,new NullWordnetOperator(),new NullStemmer());
	}
	
	public void put(String expert, double weight){
		_expertToWeight.put(expert, weight);
	}
	
	public boolean belongsToCategory(String question, boolean positiveTest){
		Set<String> positivePredictors = new HashSet<String>();
		List<String> tokens = new WordsExtractor(_wordnet,_stemmer).extractFromUsing(question,Main.N_WORDS);
        double evaluation = 0;
        for (int i=0; i<tokens.size(); i++) {
                String each = tokens.get(i);
                double weight = 0;
                if(_expertToWeight.get(each)!=null){
                	weight = _expertToWeight.get(each);
                    positivePredictors.add(each);
                }                       
                evaluation+=weight;
        }
        
        boolean positivePrediction = (evaluation>=currentThreshold());
        refreshWeightsUsing(positivePredictors, positivePrediction, positiveTest);
        
        if(positivePrediction){
                if(positiveTest) _matrix.addTruePositives(1);
                if(!positiveTest) _matrix.addFalsePositives(1);
        }
        if(!positivePrediction){
                if(positiveTest) _matrix.addFalseNegatives(1);
                if(!positiveTest) _matrix.addTrueNegatives(1);
        }
        return positivePrediction;
	}

	private void refreshWeightsUsing(Set<String> positivePredictors, boolean positivePrediction, boolean positiveTest) {
		if(positivePrediction&&positiveTest) return;
		if(!positivePrediction&&!positiveTest) return;
		

		for (String each : positivePredictors) {
			double weight = _expertToWeight.get(each);
			if (!positiveTest) weight = weight/2;
			else weight = 2*weight;
			
			_expertToWeight.put(each, weight);
		}
	}

	private double currentThreshold() {
		double threshold = 0;
		for (String each : _expertToWeight.keySet()) {
			double weight = _expertToWeight.get(each);
			threshold+=weight;
		}
		
		return threshold*_thresholdCoefficient;
	}

//	private double threshold() {
////		double threshold = _expertToWeight.size();
//		double threshold = 0;
//		for (String each : _expertToWeight.keySet()) {
//			double weight = _expertToWeight.get(each);
//			threshold+=weight;
//		}
//		
//		return threshold*_thresholdCoefficient;
//	}
	
	public String category(){
		return _category;
	}

	public List<String> bestExperts(int positions) {
		List<String> experts = new ArrayList<String>();
		if(positions<=0) return experts;
		for (int i = 0; i < positions; i++) {
			double bestWeight = -1;
			String bestExpert = "";
			for (String each : _expertToWeight.keySet()) {
				double currentWeight = _expertToWeight.get(each);
				if (currentWeight>bestWeight&&!experts.contains(each)) {
						bestExpert = each;
						bestWeight = currentWeight;
				}
			}
			experts.add(bestExpert);
		}
		
		List<String> finalResult = new ArrayList<String>();
		for (String each : experts) {
			finalResult.add(each+"\t---->\t"+_expertToWeight.get(each));
		}
		return finalResult;
	}

	public void putAllTermsAndUpdate(Map<String, Integer> allTermsToNumberOfCategories) {
		for (String term : _expertToWeight.keySet()) {
			Integer totalOccurences = allTermsToNumberOfCategories.get(term);
			
			if(totalOccurences==null) totalOccurences = 0;
			
			totalOccurences++;			
			allTermsToNumberOfCategories.put(term, totalOccurences);
		}
	}

	public void makeTFIDFOnUsing(String term, int totalOccurences, int categories) {
		if(_expertToWeight.get(term)==null) return;
		
//		totalOccurences = (int)Math.pow(totalOccurences, 10);
//		categories = (int)Math.pow(categories, 10);
		double logArgument = (double)categories/totalOccurences;
//		double idf = Math.log(logArgument);
		double idf = logArgument;
		
		double weight = _expertToWeight.get(term);
		weight*=idf;
		
		_expertToWeight.put(term, weight);
	}

	public void setThresholdCoefficient(double coefficient) {
		_thresholdCoefficient = coefficient;
	}

	public void writeResultsForOn(int testQuestions, TextFile file) {
		_matrix.writeResultsForOn(testQuestions, _category, file);
	}

	public BinaryConfusionMatrix binaryMatrix() {
		return _matrix;
	}

}
